﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Data.Services.Client;
using System.Reflection;

namespace Xceed.Silverlight.Data
{
  public sealed class WcfDataServicesDataSourceProvider : WcfDataServicesDataSourceProviderBase
  {
    #region Constructors

    public WcfDataServicesDataSourceProvider( DataServiceQuery dataServiceQuery )
    {
      if( dataServiceQuery == null )
        throw new ArgumentNullException( "dataServiceQuery" );

      this.Initialize( dataServiceQuery, null, true );
    }

    public WcfDataServicesDataSourceProvider(
      DataServiceQuery dataServiceQuery,
      bool canCount )
    {
      if( dataServiceQuery == null )
        throw new ArgumentNullException( "dataServiceQuery" );

      this.Initialize( dataServiceQuery, null, canCount );
    }

    public WcfDataServicesDataSourceProvider(
      DataServiceQuery dataServiceQuery,
      DataServiceContext dataServiceContext )
    {
      if( dataServiceQuery == null )
        throw new ArgumentNullException( "dataServiceQuery" );

      this.Initialize( dataServiceQuery, dataServiceContext, true );
    }

    public WcfDataServicesDataSourceProvider(
      DataServiceQuery dataServiceQuery,
      DataServiceContext dataServiceContext,
      bool canCount )
    {
      if( dataServiceQuery == null )
        throw new ArgumentNullException( "dataServiceQuery" );

      this.Initialize( dataServiceQuery, dataServiceContext, canCount );
    }

    #endregion

    #region CanCount Property

    public override bool CanCount
    {
      get
      {
        return m_dataSourceProvider.CanCount;
      }
    }

    #endregion

    #region ElementType Protected Property

    protected internal override Type ElementType
    {
      get
      {
        return m_dataSourceProvider.ElementType;
      }
    }

    #endregion

    protected override void Initialize()
    {
      m_dataSourceProvider.Init( this.Dispatcher );

      base.Initialize();
    }

    protected override IAsyncResult BeginExecuteCountQuery(
      CountQueryParameters queryParameters,
      AsyncCallback callback,
      object state )
    {
      return m_dataSourceProvider.BeginExecuteCountQueryCore( queryParameters, callback, state );
    }

    protected override CountQueryResult EndExecuteCountQuery( IAsyncResult asyncResult )
    {
      return m_dataSourceProvider.EndExecuteCountQueryCore( asyncResult );
    }

    protected override IAsyncResult BeginExecuteDataQuery(
      DataQueryParameters queryParameters,
      AsyncCallback callback,
      object state )
    {
      return m_dataSourceProvider.BeginExecuteDataQueryCore( queryParameters, callback, state );
    }

    protected override DataQueryResult EndExecuteDataQuery( IAsyncResult asyncResult )
    {
      return m_dataSourceProvider.EndExecuteDataQueryCore( asyncResult );
    }

    private void Initialize(
      DataServiceQuery dataServiceQuery,
      DataServiceContext dataServiceContext,
      bool forceCount )
    {
      m_dataSourceProvider = WcfDataServicesDataSourceProvider.CreateDataSourceProvider(
                               dataServiceQuery,
                               dataServiceContext,
                               forceCount );
    }

    private static WcfDataServicesDataSourceProviderBase<TElement> CreateDataSourceProvider<TElement>(
      DataServiceQuery<TElement> dataServiceQuery,
      DataServiceContext dataServiceContext,
      bool forceCount )
    {
      // Check if we are allowed to call IncludeTotalCount ourself.
      if( ( forceCount ) && ( !WcfDataServicesDataSourceProvider.IsCountingEnabled( dataServiceQuery ) ) )
      {
        try
        {
          dataServiceQuery = dataServiceQuery.IncludeTotalCount();
        }
        catch( Exception )
        {
          // We were not able to activate counting ourself.
          // Use the DataServiceQuery that was given by the user instead.
        }
      }

      return ( dataServiceContext != null )
               ? new WcfDataServicesDataSourceProvider<TElement>( dataServiceQuery, dataServiceContext )
               : new WcfDataServicesDataSourceProvider<TElement>( dataServiceQuery );
    }

    private static WcfDataServicesDataSourceProviderBase CreateDataSourceProvider(
      DataServiceQuery dataServiceQuery,
      DataServiceContext dataServiceContext,
      bool forceCount )
    {
      Type genericDataServiceQueryType = typeof( DataServiceQuery<> );
      genericDataServiceQueryType = genericDataServiceQueryType.MakeGenericType( dataServiceQuery.ElementType );

      // The current DataSourceProvider works only if the DataServiceQuery is 
      // of type DataServiceQuery<>.
      if( genericDataServiceQueryType.IsAssignableFrom( dataServiceQuery.GetType() ) )
      {
        MethodInfo currentMethodInfo = ( MethodInfo )MethodBase.GetCurrentMethod();

        foreach( MethodInfo methodInfo in currentMethodInfo.ReflectedType.GetMethods( BindingFlags.Static | BindingFlags.NonPublic ) )
        {
          if( ( methodInfo.IsGenericMethod ) && ( methodInfo.Name == currentMethodInfo.Name ) )
          {
            MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod( dataServiceQuery.ElementType );
            object dataSourceProvider = genericMethodInfo.Invoke( null, new object[] { dataServiceQuery, dataServiceContext, forceCount } );

            return ( WcfDataServicesDataSourceProviderBase )dataSourceProvider;
          }
        }
      }

      throw new NotSupportedException();
    }

    private static bool IsCountingEnabled<TElement>( DataServiceQuery<TElement> dataServiceQuery )
    {
      if( dataServiceQuery == null )
        return false;

      Uri uri = dataServiceQuery.RequestUri;

      return ( uri != null )
          && ( uri.Query.ToLowerInvariant().Contains( "$inlinecount=allpages" ) );
    }

    #region Private Fields

    private WcfDataServicesDataSourceProviderBase m_dataSourceProvider; //null

    #endregion
  }
}
